﻿// ———————————————————————–
// <copyright file="GISUtil.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// GISUtil.cs - 
// Project: "IC.NETSOA"- "ICNETRest"
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010
// Author:      Don McGarry The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using EDXLCoT;
using EDXLSharp;
using EDXLSharp.CAPLib;
using EDXLSharp.EDXLDELib;
using EDXLSharp.EDXLHAVE_2_0Lib;
using EDXLSharp.EDXLRMLib;
using EDXLSharp.NIEMCADLib;
using GISUtil.KML;
using log4net;
using MEXLSitRepLib;
using MEXLTEPLib;

namespace GISUtil
{
  public static class GISConverter
  {
    private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    /// <summary>
    /// Creates the KML representation of the given <see cref="EDXLSharp.EDXLDELib.ContentObject"/>
    /// </summary>
    /// <param name="edxlde">The distribution element wrapping the content object</param>
    /// <param name="content">The content object to represent as KML</param>
    /// <param name="idToUse">The hash/Id to use</param>
    /// <returns>KML representation of the given content object</returns>
    public static string CreateKMLForContentObject(EDXLDE edxlde, ContentObject content, string idToUse)
    {
      List<KMLPlacemark> placemarks = new List<KMLPlacemark>();
      if (content.XMLContent != null)
      {
        foreach (XElement doc in content.XMLContent.EmbeddedXMLContent)
        {
          XmlDocument xdoc = new XmlDocument();
          using (XmlReader xr = doc.CreateReader())
          {
            xdoc.Load(xr);
          }
          switch (doc.Name.NamespaceName)
          {
            case EDXLConstants.CAP11Namespace:
            case EDXLConstants.CAP12Namespace:
              CAP capmsg = new CAP();
              capmsg.ReadXML(xdoc.FirstChild);
              placemarks.Add(new KMLPlacemark(idToUse, edxlde.SenderID, capmsg));
              break;
            case EDXLConstants.HAVE10Namespace:
              EDXLHAVE havemsg = new EDXLHAVE();
              havemsg.ReadXML(xdoc.FirstChild);
              placemarks.Add(new KMLPlacemark(idToUse, edxlde.SenderID, havemsg));
              break;
            case EDXLConstants.SitRep10Namespace:
              SitRep sitRepmsg = new SitRep();
              sitRepmsg.ReadXML(xdoc.FirstChild);
              placemarks.Add(new KMLPlacemark(idToUse, edxlde.SenderID, sitRepmsg));
              break;
            case EDXLConstants.EDXLCoTNamespace:
              CoTWrapper cotmsg = new CoTWrapper();
              cotmsg.ReadXML(xdoc.FirstChild);
              placemarks.Add( new KMLPlacemark(idToUse, edxlde.SenderID, cotmsg));
              break;
            case EDXLConstants.CADNamespace:
              CADEvent cadEvent = new CADEvent();
              cadEvent.ReadXML(xdoc.FirstChild);
              placemarks.Add(new KMLPlacemark(idToUse, edxlde.SenderID, cadEvent));
              break;
            default:
              ValueList contentKeywordVL = content.ContentKeyword.FirstOrDefault(v => v.ValueXML.Equals(EDXLConstants.ContentKeywordListName));
              if (contentKeywordVL != null)
              {
                OtherXMLWrapper xmlMsg = new OtherXMLWrapper(contentKeywordVL.Value, content.ContentDescription);
                xmlMsg.ReadXML(xdoc.FirstChild);
              }
              //TODO: What is the correct way to handle this?
              //return string.Empty;
              break;
          }
        }
      }
      else if (content.NonXMLContent != null)
      {
        string author = edxlde.SenderID;
        DateTime sent = edxlde.DateTimeSent;
        string loc = edxlde.TargetArea[0].Circle[0];
        string[] coords = loc.Split(' ');
        int lat = int.Parse(coords[0]);
        int lon = int.Parse(coords[1]);
        KMLPoint point = new KMLPoint(lat, lon);

        placemarks.Add(new KMLPlacemark(idToUse, author, content, sent, point));
      }
      /*************
       * Kind of an ugly hack to generate the KML placemark XML *without* the xml declaration,
       * since having the xml declaration breaks the KML feed. See links for details:
       * http://stackoverflow.com/questions/9332558/how-can-i-skip-xml-declaration-when-serializing
       * http://stackoverflow.com/questions/19045921/net-xmlserialize-throws-writestartdocument-cannot-be-called-on-writers-created
       * ***********/
      StringWriter writer = new StringWriter();
      XmlWriter xw = XmlWriter.Create(writer, new XmlWriterSettings() { OmitXmlDeclaration = true, ConformanceLevel = System.Xml.ConformanceLevel.Fragment });
      xw.WriteWhitespace("");
      XmlSerializer xs = new XmlSerializer(typeof(KMLPlacemark));
      foreach (KMLPlacemark placemark in placemarks)
      {
        xs.Serialize(xw, placemark);
      }
      xw.Flush();
      xw.Close();

      string result = writer.ToString();
      Log.Info("Created KML Placemark(s):\r\n" + result);
      return result;
    }

    public static string CreateGeoRSSForContentObject(EDXLDE edxlde, ContentObject co)
    {
      //todo
      throw new NotImplementedException();
    }

    /// <summary>
    /// Make a GEORSS feed from an XML content object
    /// </summary>
    /// <param name="co">The content object to be syndicated.</param>
    /// <returns>A list, where each object in the list is a Syndication item on the feed.</returns>
    public static IEnumerable<SyndicationItem> GetXMLContentSyndicationItems(ContentObject co)
    {
      if (co == null)
      {
        throw new ArgumentNullException("co");
      }

      Guid contentKey = new Guid();
      List<SyndicationItem> synItems = new List<SyndicationItem>();

      if (co.XMLContent != null && co.ContentKeyword != null)
      {
        ValueList contentType = co.ContentKeyword.FirstOrDefault(ck => ck.ValueListURN == EDXLConstants.ContentKeywordListName);
        if (contentType != null)
        {
          foreach (XElement elt in co.XMLContent.EmbeddedXMLContent)
          {
            if (contentType.Value.Contains("CAP"))
            {
              new CAP(elt.ToString()).ToGeoRSS(synItems, contentKey);
            }
            else if (contentType.Value.Contains("HAVE"))//Exclude the others that cotnain "HAVE"
            {
              using (XmlReader xr = elt.CreateReader())
              {
                XmlDocument doc = new XmlDocument();
                doc.Load(xr);
                EDXLHAVE have = new EDXLHAVE();
                have.ReadXML(doc);
                have.ToGeoRSS(synItems, contentKey);
              }
            }
            else if (contentType.Value.Contains("EDXL-RM"))//Exclude the others that cotnain "EDXL-RM"
            {
              using (XmlReader xr = elt.CreateReader())
              {
                XmlDocument doc = new XmlDocument();
                doc.Load(xr);
                ResourceMessageType rm = new ResourceMessageType();
                rm.ReadXML(doc);
                rm.ToGeoRSS(synItems, contentKey);
              }
            }
            else if (contentType.Value.Contains("EDXL-SitRep"))//Exclude the others that cotnain "EDXL-SitRep"
            {
              using (XmlReader xr = elt.CreateReader())
              {
                XmlDocument doc = new XmlDocument();
                doc.Load(xr);
                EDXLSharp.EDXLSitRepLib.SitRep sitrep = new EDXLSharp.EDXLSitRepLib.SitRep();
                sitrep.ReadXML(doc);
                sitrep.ToGeoRSS(synItems, contentKey);
              }
            }
            else if (contentType.Value.Contains("SitRep"))//Exclude "MEXL-SitRep", which cotnains "SitRep"
            {
              using (XmlReader xr = elt.CreateReader())
              {
                XmlDocument doc = new XmlDocument();
                doc.Load(xr);
                MEXLSitRepLib.SitRep sitrep = new SitRep();
                sitrep.ReadXML(doc);
                sitrep.ToGeoRSS(synItems, contentKey);
              }
            }
            else if (contentType.Value.Contains("TEP"))//Exclude "MEXL-TEP", which cotnains "TEP"
            {
              using (XmlReader xr = elt.CreateReader())
              {
                XmlDocument doc = new XmlDocument();
                doc.Load(xr);
                TEP tep = new TEP();
                tep.ReadXML(doc);
                tep.ToGeoRSS(synItems, contentKey);
              }
            }
            else if (contentType.Value.Contains("CoTEvent"))
            {
              using (XmlReader xr = elt.CreateReader())
              {
                XmlDocument doc = new XmlDocument();
                doc.Load(xr);
                CoTWrapper cot = new CoTWrapper();
                cot.ReadXML(doc);
                cot.ToGeoRSS(synItems, contentKey);
              }
            }
            else if (contentType.Value.Contains("CAD"))
            {
              new CADEvent(elt.ToString()).ToGeoRSS(synItems, contentKey);
            }
            else //other XML type
            {
              using (XmlReader xr = elt.CreateReader())
              {
                XmlDocument doc = new XmlDocument();
                doc.Load(xr);
                OtherXMLWrapper oxw = new OtherXMLWrapper(contentType.Value, co.ContentDescription);
                oxw.ReadXML(doc.FirstChild);
                oxw.ToGeoRSS(synItems, contentKey);
              }
            }
          }
        }
      }
      
      return synItems;
    }

    /// <summary>
    /// Creates a GeoRSS feed from a nonXML content object
    /// </summary>
    /// <param name="co">The content object to be syndicated.</param>
    /// <returns>A list, where each object in the list is a Syndication item on the feed.</returns>
    public static IEnumerable<SyndicationItem> GetNonXMLContentSyndicationItems(ContentObject co)
    {
      if (co == null)
      {
        throw new ArgumentNullException("co");
      }
      Guid contentKey = new Guid();
      List<SyndicationItem> items = new List<SyndicationItem>();
      if (co.NonXMLContent != null)
      {
        co.NonXMLContent.ToGeoRSS(items, contentKey);
      }
      return items;
    }
  }
}